#ifndef __RSTP_TRANSFER_H_FILE_983673jf__
#define __RSTP_TRANSFER_H_FILE_983673jf__


#ifdef WIN32
#pragma warning(disable:4786)
#endif

#include "UDPSocket.h"
#include "OSMutex.h"
#include "PacketCombiner.h"

#include <map>

enum Read_ErrCode{
	READ_ERR_TIMEOUT = -2
};

struct SPeerAddress;
class CRstpTransfer
{
public:
	CRstpTransfer();
	~CRstpTransfer();

	static UInt16		GenerateReqID();
	Bool		Initialize();
	/*
	* UDP Bind. CAN CALL more times
	* INPUT:
	* OUTPUT:
	*	outHandle: general it is socket fd
	* RETURN:
	*	TRUE: ok
	*	FALSE: failed
	*/
	Bool		Bind(UInt32 inLocalIP, UInt16 inLocalPort, Int32 *outHandle);
	
	/*
	* Send Data.
	* INPUT:
	*	inHandle: come from ReadDataFrom, used to select different dst.
	*		>0:  valid
	*		<=0: system automatcally select
	* OUTPUT:
	* RETURN:
	*/
	OS_Error        SendDataTo(UInt32 inDestAddr, UInt16 inDestPort, void* inBuffer, UInt32 inLength, Int32 inHandle = 0);
	
	/*
	 * Seperate data to several if neccessary
	 * NOTE:
	 *	1. if NAT or data packet. send it directly(no segment) else add SRstpCommHead and send
	 * 	2. inLength/ inSegmentSize <= 65535
	 * INPUT:
	 *	inHandle: come from ReadDataFrom, used to select different dst.
	 *		>0:  valid
	 *		<=0: system automatcally select
	 * OUTPUT:
	 * RETURN:
	 */
	OS_Error		SendDataToExt(UInt8 inChanID,UInt16 inRequestID,UInt16 inSegmentSize,UInt32 inDestAddr, UInt16 inDestPort, void* inBuffer, UInt32 inLength, Int32 inHandle = 0);
	
	/*
	* RecvData From. 
	* INPUT:
	* OUTPUT:
	*	outHandle: a id used to identify a source path that data comming from
	* RETURN:
	*/
	OS_Error        ReadDataFrom(UInt32* outSourceIP, UInt16* outSourcePort, void* ioBuffer, UInt32 inBufLen, UInt32* outRecvLen, Int32 *outHandle,UInt32 nTimeout=100);

	/* 
	 * ReadData
	 * if NAT or data, pass it directly to up layer(include SRstpCommHead) else automatically combine data and pass it(NOT INCLUDE SRstpCommHead)
	 * INPUT:
	 *	inBufLen: should big enough
	 *	nTimeOut: metric - ms
	 * OUTPUT:
	 *	outBuff: act data point
	 *	outRecvLen: act data len
	 *	outHandle: a id used to identify a source path that data comming from
	 * RETURN:
	 *	OS_NoErr: packet is complete
	 *	READ_ERR_TIMEOUT: has not complete packet or read timeout
	 *	OS_Err: fetal error
	 */
	OS_Error		ReadDataFromExt(UInt32* outSourceIP, UInt16* outSourcePort, void* ioBuffer, UInt32 inBufLen, 
						UInt8 *outChanID, UInt16 *outReqID, char* &outBuff,UInt32* outRecvLen, Int32 *outHandle, UInt32 nTimeout=100);
	
	/*
	* According to inHandle to find related fd
	* INPUT:
	*	inHandle:  >0 is valid
	*		<=0 system will automaticall return a valid one
	* OUTPUT:
	*	outAddr: only outAddr.CommIP, outAddr.CommPort is valid
	* RETURN:
	*	TRUE: OK
	*	FALSE: failed
	*/
	Bool            GetLocalAddress(Int32 inHandle,SPeerAddress &outAddr);

	/*
	* According to inHandle to find related fd
	* INPUT:
	*	inHandle:  >0 is valid
	*		<=0 system will automaticall return a valid one
	* OUTPUT:
	* RETURN:
	*	>0 is valid
	*	<=0 failed
	*/
	Int32           GetUdpFd(Int32 inHandle);
	
	/*
	OS_Error        JoinMulticastGroup(UInt32 nMultiIP);
	OS_Error        SetMulticastGroup(UInt32 nMultiIP);
	void            LeaveMulticastGroup(UInt32 nMultiIP);
	*/

	/*
	 *	According to remote host info to select a good handle
	 *	NOTE:
	 *		!!!!!WARNING! TEMP, only select the first fd. NEED to implement!!!!!
	 * INPUT:
	 * OUTPUT:
	 * RETURN:
	 *	Handle: >0 is valid
	 *		<=0  failed
	 */
	Int32			GetHandle(UInt32 inRemoteIP, UInt16 inRemotePort);
	
private:
	inline UDPSocket*	FindSocket(Int32 inHandle);
	
private:
	//key is fd
	typedef std::map<Int32,UDPSocket *>	UDPSocketMap;
	typedef UDPSocketMap::iterator	UDPSocketMapIterator;
	
	UDPSocketMap	m_mapSockets;
	fd_set			m_fdSet;
	Int32			m_maxFds;
	
	UInt32			m_UsedCount;
	Bool			m_IsAutoAllocPort;

	enum {BUFFER_SIZE = 2048};
	char			m_Buffer[BUFFER_SIZE];
	
	/*
	* NOTE: 
	*	1. operator = is DANGEROUS!!!
	*	2. since CPacketCombiner does not support re-use, so NOTE RE-USE
	*/
	struct SCompleteData
	{
		CPacketCombiner	*pCombiner;
		Int32		TimeOut;
		UInt32		StartTime;	//second
		
		SCompleteData()
		{
			pCombiner = new CPacketCombiner;
			TimeOut = 0;
			StartTime = 0;
		}
		~SCompleteData()
		{
			if(pCombiner)
				delete pCombiner;
		}
	};
	// key is ChanID<<16+RequestID
	typedef std::map<UInt32,SCompleteData *>	RecvDataMap;
	typedef RecvDataMap::iterator		RecvDataMapIterator;
	RecvDataMap		m_mapRecvData;
	
	static UInt16		m_nReqIDCounter;
	static OSMutex		m_mtxReqIDCounter;
};

inline UDPSocket* CRstpTransfer::FindSocket(Int32 inHandle)
{
	UDPSocket *pSocket = NULL;
	if(inHandle <= 0)
	{
		if(!m_mapSockets.empty())
			pSocket = m_mapSockets.begin()->second;
	}
	else
	{
		UDPSocketMapIterator it = m_mapSockets.find(inHandle);
		if(it != m_mapSockets.end())
			pSocket = it->second;
	}
	return pSocket;
}


#endif

